package com.z.xviewpager.xviewpager;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by z
 * on 2017/11/19 0019.
 */

public class XViewPager extends ViewGroup {

    private XAdapter mAdapter;  //适配器

    private View mPreviousView; //上一个视图
    private View mCurrentView;  //当前的视图
    private View mNextView;     //下一个视图

    private int mPreviousPosition;      //上一个视图的数据源position
    private int mCurrentPosition = 0;   //当前视图的数据源position
    private int mNextPosition;          //下一个视图的数据源position

    private int mCurrentChildLeft = 0;  //当前视图在布局中的left,这个值决定了视图如何滑动

    private float mTouchStartX; //手指按下去的起始X坐标
    private VelocityTracker mVelocityTracker;   ///速度跟踪器,用于在手指释放后决定滚动方向
    private int mPointerId; //手指按下去的第一个点
    private float mReleaseVelocityX;    //X方向释放速度
    private float mSensitivity = 1600;       //敏感度PX/s,释放速度大于这个值或者拖拽距离大于某个值切换界面
    private long mAnimationDuration = 200;   //页面切换动画时间ms

    private OnPageChangeListener mOnPageChangeListener; //页面改变监听器

    public XViewPager(Context context) {
        super(context);
    }

    public XViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public XViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //Log.i("xViewPager", "onLayout");
        if (null != mCurrentView) {
            mCurrentView.layout(mCurrentChildLeft, 0, mCurrentChildLeft + getWidth(), getHeight());
        }
        if (null != mPreviousView) {
            mPreviousView.layout(mCurrentChildLeft - getWidth(), 0, mCurrentChildLeft, getHeight());
        }
        if (null != mNextView) {
            mNextView.layout(mCurrentChildLeft + getWidth(), 0, mCurrentChildLeft + 2 * getWidth(), getHeight());
        }
    }

    public void setAdapter(XAdapter adapter) {
        this.mAdapter = adapter;
        inflateView();
        addViewToViewGroup();
    }

    /**
     * 设置页面改变监听.
     *
     * @param onPageChangeListener 监听器实例
     */
    public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
        this.mOnPageChangeListener = onPageChangeListener;
    }

    /**
     * 填充View.
     */
    private void inflateView() {
        mPreviousPosition = mAdapter.getPreviousPosition(mCurrentPosition);
        mNextPosition = mAdapter.getNextPosition(mCurrentPosition);
        if (mPreviousPosition >= 0 && mPreviousPosition <= mAdapter.getSize() - 1) {
            mPreviousView = mAdapter.getView(mPreviousPosition, mPreviousView, this);
        }
        if (mCurrentPosition >= 0 && mCurrentPosition <= mAdapter.getSize() - 1) {
            mCurrentView = mAdapter.getView(mCurrentPosition, mCurrentView, this);
        }
        if (mNextPosition >= 0 && mNextPosition <= mAdapter.getSize() - 1) {
            mNextView = mAdapter.getView(mNextPosition, mNextView, this);
        }
    }

    /**
     * 添加View到ViewGroup.
     * 请确定各个引用正确的指向了它们的实例,在addView的时候是添加实例,在onLayout的时候是根据引用布局.
     */
    private void addViewToViewGroup() {
        if (null != mPreviousView) {
            this.addView(mPreviousView);
        }
        if (null != mCurrentView) {
            this.addView(mCurrentView);
        }
        if (null != mNextView) {
            this.addView(mNextView);
        }
        if (null != mOnPageChangeListener) {
            mOnPageChangeListener.onPageChange(mCurrentPosition);
        }
    }

    /**
     * 滚动到上一个.
     *
     * @param move 当前已经滚动的距离
     */
    private void moveToPrevious(int move) {
        //设置已经移动的距离为起始值,最终位置为结束值
        ValueAnimator animator = ValueAnimator.ofInt(move, getWidth()); //属性动画滚动View
        animator.setDuration(mAnimationDuration); //设置动画时长
        animator.setRepeatCount(0); //设置动画重复次数
        animator.start();           //开始动画
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { //动画更新监听
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentChildLeft = (int) animation.getAnimatedValue(); //更改mCurrentView的left
                XViewPager.this.requestLayout();    //请求重新布局
            }
        });
        animator.addListener(new Animator.AnimatorListener() {  //动画状态监听
            @Override
            public void onAnimationStart(Animator animation) {

            }

            /**
             * 动画结束时对XViewPager进行设置
             * @param animation 动画实例的引用
             */
            @Override
            public void onAnimationEnd(Animator animation) {
                //滚动完成后mNextPosition是滚动完成前的mCurrentPosition
                mNextPosition = mCurrentPosition;
                //滚动完成后mCurrentPosition是滚动完成前的mPreviousPosition
                mCurrentPosition = mPreviousPosition;
                //根据滚动完成后的mCurrentPosition获取滚动完成后的mPreviousPosition
                mPreviousPosition = mAdapter.getPreviousPosition(mCurrentPosition);
                View view = mNextView;  //使用一个view引用指向滚动完成前的mNextView实例
                //移除滚动完成前的mNextView实例,这里是根据实例来移除!!!这个实例没有存在于ViewGroup的意义了
                XViewPager.this.removeView(mNextView);
                //滚动完成后的mNextView指向的实例是滚动完成前mCurrentView指向的实例,这里将mNextView引用指
                // 向了滚动完成前的mCurrentView的实例,这个实例并没有从ViewGroup中移除,将mNextView引用指向
                // mCurrentView仅仅是为了方便onLayout布局
                mNextView = mCurrentView;
                mCurrentView = mPreviousView;   //这个原理同上一行代码的原理
                //经过上面三行蛇皮代码,此时这个ViewGroup中还有两个View的实例,分别是滚动完成前mCurrentView
                // 的实例(现在是mNextView引用指向它),滚动完成前mPreviousView的实例(现在是mCurrentView引用
                // 指向它).现在还少了一个实例,应该是一个滚动完成后的mCurrentView实例左侧的View实例,构建这个
                // 实例并使用mPreviousView引用指向它.这里使用mAdapter来获取这个View的实例,为了优化性能,不
                // 用反复创建对象并findViewById(),所以提供一个实例给mAdapter的getView()方法,这个实例就是
                // 之前从这个ViewGroup中移除的实例,现在这个实例是view引用指向它,将view这个引用传递
                // 给getView().使用mPreviousView引用指向获取到的新的实例
                mPreviousView = mAdapter.getView(mPreviousPosition, view, XViewPager.this);
                XViewPager.this.addView(mPreviousView); //将这个实例通过mPreviousView引用添加到ViewGroup
                //此时各个引用都指向了它们应该指向的实例,将mCurrentView的left置0
                mCurrentChildLeft = 0;
                XViewPager.this.requestLayout();    //请求重新布局
                if (null != mOnPageChangeListener) {
                    mOnPageChangeListener.onPageChange(mCurrentPosition);
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

    /**
     * 滚动到下一个
     *
     * @param move 当前已经滚动的距离
     * @see #moveToPrevious(int) 原和这个函数相同
     */
    private void moveToNext(int move) {
        ValueAnimator animator = ValueAnimator.ofInt(move, -getWidth());
        animator.setDuration(mAnimationDuration);
        animator.setRepeatCount(0);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentChildLeft = (int) animation.getAnimatedValue();
                XViewPager.this.requestLayout();
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mPreviousPosition = mCurrentPosition;
                mCurrentPosition = mNextPosition;
                mNextPosition = mAdapter.getNextPosition(mCurrentPosition);
                View view = mPreviousView;
                XViewPager.this.removeView(mPreviousView);
                mPreviousView = mCurrentView;
                mCurrentView = mNextView;
                mNextView = mAdapter.getView(mNextPosition, view, XViewPager.this);
                XViewPager.this.addView(mNextView);
                mCurrentChildLeft = 0;
                XViewPager.this.requestLayout();
                if (null != mOnPageChangeListener) {
                    mOnPageChangeListener.onPageChange(mCurrentPosition);
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

    /**
     * 释放后拖拽距离或者释放速度不够就滚回去.
     *
     * @param move move
     */
    private void scrollBack(int move) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(move, 0);
        valueAnimator.setDuration(mAnimationDuration);
        valueAnimator.setRepeatCount(0);
        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentChildLeft = (int) animation.getAnimatedValue();
                XViewPager.this.requestLayout();
            }
        });
    }

    /**
     * 手指释放后自动滚动.
     *
     * @param move             手指已经滚动的距离
     * @param releaseVelocityX 释放时X方向的速度
     */
    private void releaseAutoScroll(float move, float releaseVelocityX) {
        if (Math.abs(move) < getWidth() / 2 && Math.abs(releaseVelocityX) < mSensitivity) {
            scrollBack((int) move);
        } else {
            if (move > 0) {
                moveToPrevious((int) move);
            } else {
                moveToNext((int) move);
            }
        }
    }

    /**
     * 设置当前条目.
     *
     * @param item 设置的当期条目
     * @throws IndexOutOfBoundsException 设置当前条目的操作可以在设置Adapter之前或之后.在之前设置不能知道
     *                                   设置的item是否合法,所以这里不做判断.
     */
    public void setCurrentItem(int item) {
        mCurrentPosition = item;
        //当前的mCurrentView不为null说明这个函数是在执行完setAdapter之后调用,此时需要先移除原来的View,
        // 添加新的View
        if (null != mCurrentView) {
            if (null != mPreviousView) {
                this.removeView(mPreviousView);
            }
            if (null != mCurrentView) {
                this.removeView(mCurrentView);
            }
            if (null != mNextView) {
                this.removeView(mNextView);
            }
            inflateView();
            addViewToViewGroup();
        }
    }

    /**
     * 滚动到上一个条目.
     */
    public void scrollToPreviousView() {
        moveToPrevious(0);  //滚动到上一个条目,拖拽距离为0
    }

    /**
     * 滚动到下一个条目.
     */
    public void scrollToNext() {
        moveToNext(0);  //滚动到下一个条目,拖拽距离为0
    }


    /**
     * 获取一个速度跟踪器.
     *
     * @param event 将event添加到速度跟踪器
     */
    private void acquireVelocityTracker(final MotionEvent event) {
        if (null == mVelocityTracker) {
            mVelocityTracker = VelocityTracker.obtain();    //获取速度跟踪器
        }
        mVelocityTracker.addMovement(event);    //给速度跟踪器添加MotionEvent
        mVelocityTracker.computeCurrentVelocity(1000);  //设置单位PX/s
    }

    /**
     * 释放速度跟踪器的资源.
     */
    private void releaseVelocityTracker() {
        if (null != mVelocityTracker) {
            mVelocityTracker.clear();
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        acquireVelocityTracker(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = event.getPointerId(0);  //第一个按下去的触点ID,可能有多个,但是至少有一个
                mTouchStartX = event.getX();
                requestLayout();
                Log.i("xViewPager", "onTouchEvent DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                mCurrentChildLeft = (int) (event.getX() - mTouchStartX);
                XViewPager.this.requestLayout();
                mReleaseVelocityX = mVelocityTracker.getXVelocity(mPointerId);
                Log.i("xViewPager", "onTouchEvent MOVE,velocityX = " + mReleaseVelocityX);
                break;
            case MotionEvent.ACTION_UP:
                releaseAutoScroll(event.getX() - mTouchStartX, mReleaseVelocityX);
                releaseVelocityTracker();   //释放速度跟踪器的资源
                Log.i("xViewPager", "onTouchEvent UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                releaseVelocityTracker();   //释放速度跟踪器的资源
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 页面改变监听接口.
     */
    public interface OnPageChangeListener {
        /**
         * 页面改变是调用这个方法.
         *
         * @param position 改变后的页面在所使用数据源的position
         */
        void onPageChange(int position);
    }

}
